前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# GET请求数据获取

30 秒速记

  • GET 参数在 URL 查询串里:ctx.query 拿对象,ctx.querystring 拿原始字符串
  • 值全是字符串:?page=2 拿到的是 '2',要自己转数字;没传就是 undefined
  • 重复键变数组:?tag=a&tag=b → { tag: ['a', 'b'] };不支持嵌套,a[b]=1 的键就是 'a[b]'
  • 分页给默认值和上限,排序字段走白名单,别拼进 SQL
  • token、手机号别放查询串,会进访问日志、浏览器历史和 Referer

Koa 里拿 GET 参数很直接:ctx.query 是解析好的对象,ctx.querystring 是 ? 后面那串原始文本。 容易踩的是类型:查询串里的值全是字符串,?page=2&size=20 拿到的是 { page: '2', size: '20' },同一个键传两次会变成数组,没传就是 undefined。所以我会在接口入口统一做一层校验和转换,分页给默认值和上限,排序字段只认白名单。敏感信息别放查询串里,它会出现在访问日志、浏览器历史和 Referer 里。

理解「GET请求数据获取」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

列表接口里处理查询参数,一般就是下面这几步:转类型、给默认值、限上限、走白名单。

app.use(async ctx => {
  // GET /list?page=2&size=500&sort=price&tag=a&tag=b
  const page = Math.max(1, parseInt(ctx.query.page, 10) || 1)
  const size = Math.min(100, parseInt(ctx.query.size, 10) || 20)   // 上限 100
  const sort = ['price', 'createdAt'].includes(ctx.query.sort) ? ctx.query.sort : 'createdAt'
  const tags = [].concat(ctx.query.tag ?? [])                       // 单个值或数组都统一成数组

  ctx.body = { page, size, sort, tags }
  // => { page: 2, size: 100, sort: 'price', tags: ['a', 'b'] }
})
@前端进阶之旅: 代码已经复制到剪贴板

几个细节:size 不设上限,有人传 size=100000 就能把数据库拖慢;sort 直接拼进 ORDER BY 是典型的注入点,只能从白名单里取;tag 可能是字符串也可能是数组,用 [].concat() 统一一下,后面的代码就不用到处判断。字段一多,换成 zod 写一个 schema,转换和校验一次搞定。

面试官追问

追问 1GET /search 什么参数都没带,ctx.query 是什么?
参考回答

空对象 {},不是 undefined,也不该报错。按业务给默认值,或者回 400 提示缺参数,别直接 500。

追问 2?page=2 和 ?page=2&page=3,ctx.query.page 分别是什么?
参考回答

前者是字符串 '2',后者是数组 ['2', '3']。拿到值先判断类型,字段多了用 zod 这类 schema 库统一校验,别在每个接口里手写一堆 if。

追问 3前端传 ?filter[status]=done,后端拿到的是什么?
参考回答

{ 'filter[status]': 'done' },键名就是整串。Koa 默认用 Node 的 querystring 解析,不支持嵌套;需要嵌套就用 koa-qs,或者让前端把复杂条件放到 POST 的 JSON 里。

追问 4ctx.query.hasOwnProperty('page') 为什么直接报错?
参考回答

querystring.parse 返回的是没有原型的对象,上面根本没有 hasOwnProperty。改用 Object.hasOwn(ctx.query, 'page') 或 'page' in ctx.query。

追问 5用户的搜索词放在查询串里,要注意什么?
参考回答

它会进 Nginx 访问日志、浏览器历史,还会通过 Referer 带给第三方。普通搜索词问题不大,token、手机号这类就别放;日志里要记也先脱敏。

# GET请求数据获取

# 使用方法

30 秒速记

  • 两组四个入口:ctx.query / ctx.querystring 和 ctx.request.query / ctx.request.querystring,前者代理后者,值一样
  • query → 对象 { a: '1', b: '2' },值是字符串;querystring → 'a=1&b=2',不带 ?
  • 带 ? 的是 ctx.search;路径加查询串是 ctx.url
  • 业务判断用对象,签名校验、原样透传用原始字符串
  • 给 ctx.query 赋值会同步改写 querystring 和 url

Koa 拿 GET 参数就两种形态:query 是解析好的对象,querystring 是原始字符串,两者都能从 ctx 或 ctx.request 上取。 ctx.query 其实就是代理到 ctx.request.query,两种写法拿到的是同一个东西,团队统一一种就行,我习惯直接写 ctx.query。请求 /?a=1&b=2 时,ctx.query 是 { a: '1', b: '2' },注意值是字符串不是数字;ctx.querystring 是 'a=1&b=2',不带问号。业务逻辑用对象,要拿原始文本去算签名时才用 querystring。

在koa中,获取GET请求数据源头是koa中request对象中的query方法或querystring方法,query返回是格式化好的参数对象,querystring返回的是请求字符串,由于ctx对request的API有直接引用的方式,所以获取GET请求数据有两个途径。

  • 1.是从上下文中直接获取
    • 请求对象ctx.query,返回如 { a:1, b:2 }
    • 请求字符串 ctx.querystring,返回如 a=1&b=2
  • 2.是从上下文的request对象中获取
    • 请求对象ctx.request.query,返回如 { a:1, b:2 }
    • 请求字符串 ctx.request.querystring,返回如 a=1&b=2

面试官追问

追问 1ctx.querystring.a 为什么是 undefined?
参考回答

querystring 是字符串 'a=1&b=2',字符串上没有 a 这个属性。要拿字段用 ctx.query.a。

追问 2ctx.query.a === 1 为什么是 false?
参考回答

值是字符串 '1',严格相等比不过。写 Number(ctx.query.a) === 1,或者在入口统一转好类型。

追问 3网关要用原始参数串算签名,用 query 还是 querystring?
参考回答

用 ctx.querystring。解析成对象再拼回去,参数顺序、+ 和 %20 的写法、空值都可能变,签名就对不上了。

追问 4ctx.query 能直接赋值改参数吗?
参考回答

能,ctx.query = { a: '2' } 会反过来改写 ctx.querystring 和 ctx.url,后面的中间件看到的都是新值。一般只在做参数规范化时这么干,业务代码里别随手改。

追问 5反复读 ctx.query,会不会每次都重新解析?
参考回答

不会。Koa 按 querystring 做了缓存,同一个请求里读多少次都只解析一次,ctx.query 和 ctx.request.query 拿到的也是同一份结果。

# 举个例子

30 秒速记

  • 示例把 ctx.url、ctx.request.query、ctx.request.querystring、ctx.query、ctx.querystring 一起返回,方便对照
  • 访问 /page/user?a=1&b=2:url 是 '/page/user?a=1&b=2',query 是 { a: '1', b: '2' },querystring 是 'a=1&b=2'
  • ctx.xxx 和 ctx.request.xxx 两组输出一模一样,前者就是代理
  • 跑起来顺手试边界:重复键、空值、+、中文、不完整的 % 编码

这个例子的思路是把能拿到参数的几个入口一次性全返回,在浏览器里一眼对比它们的区别。 访问 /page/user?a=1&b=2,url 是带路径和查询串的完整地址,两个 query 都是 { a: '1', b: '2' },两个 querystring 都是 'a=1&b=2'。两组完全一致,说明 ctx.query 就是 ctx.request.query 的快捷方式。跑起来之后我建议多试几个边界输入,看看解析结果,比背文档记得牢。

demo源码

基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

GET请求数据获取|Nodejs专题系列

# GET请求数据获取

30 秒速记

  • GET 参数在 URL 查询串里:ctx.query 拿对象,ctx.querystring 拿原始字符串
  • 值全是字符串:?page=2 拿到的是 '2',要自己转数字;没传就是 undefined
  • 重复键变数组:?tag=a&tag=b → { tag: ['a', 'b'] };不支持嵌套,a[b]=1 的键就是 'a[b]'
  • 分页给默认值和上限,排序字段走白名单,别拼进 SQL
  • token、手机号别放查询串,会进访问日志、浏览器历史和 Referer

Koa 里拿 GET 参数很直接:ctx.query 是解析好的对象,ctx.querystring 是 ? 后面那串原始文本。 容易踩的是类型:查询串里的值全是字符串,?page=2&size=20 拿到的是 { page: '2', size: '20' },同一个键传两次会变成数组,没传就是 undefined。所以我会在接口入口统一做一层校验和转换,分页给默认值和上限,排序字段只认白名单。敏感信息别放查询串里,它会出现在访问日志、浏览器历史和 Referer 里。

理解「GET请求数据获取」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

列表接口里处理查询参数,一般就是下面这几步:转类型、给默认值、限上限、走白名单。

app.use(async ctx => {
  // GET /list?page=2&size=500&sort=price&tag=a&tag=b
  const page = Math.max(1, parseInt(ctx.query.page, 10) || 1)
  const size = Math.min(100, parseInt(ctx.query.size, 10) || 20)   // 上限 100
  const sort = ['price', 'createdAt'].includes(ctx.query.sort) ? ctx.query.sort : 'createdAt'
  const tags = [].concat(ctx.query.tag ?? [])                       // 单个值或数组都统一成数组

  ctx.body = { page, size, sort, tags }
  // => { page: 2, size: 100, sort: 'price', tags: ['a', 'b'] }
})
@前端进阶之旅: 代码已经复制到剪贴板

几个细节:size 不设上限,有人传 size=100000 就能把数据库拖慢;sort 直接拼进 ORDER BY 是典型的注入点,只能从白名单里取;tag 可能是字符串也可能是数组,用 [].concat() 统一一下,后面的代码就不用到处判断。字段一多,换成 zod 写一个 schema,转换和校验一次搞定。

面试官追问

追问 1GET /search 什么参数都没带,ctx.query 是什么?
参考回答

空对象 {},不是 undefined,也不该报错。按业务给默认值,或者回 400 提示缺参数,别直接 500。

追问 2?page=2 和 ?page=2&page=3,ctx.query.page 分别是什么?
参考回答

前者是字符串 '2',后者是数组 ['2', '3']。拿到值先判断类型,字段多了用 zod 这类 schema 库统一校验,别在每个接口里手写一堆 if。

追问 3前端传 ?filter[status]=done,后端拿到的是什么?
参考回答

{ 'filter[status]': 'done' },键名就是整串。Koa 默认用 Node 的 querystring 解析,不支持嵌套;需要嵌套就用 koa-qs,或者让前端把复杂条件放到 POST 的 JSON 里。

追问 4ctx.query.hasOwnProperty('page') 为什么直接报错?
参考回答

querystring.parse 返回的是没有原型的对象,上面根本没有 hasOwnProperty。改用 Object.hasOwn(ctx.query, 'page') 或 'page' in ctx.query。

追问 5用户的搜索词放在查询串里,要注意什么?
参考回答

它会进 Nginx 访问日志、浏览器历史,还会通过 Referer 带给第三方。普通搜索词问题不大,token、手机号这类就别放;日志里要记也先脱敏。

# GET请求数据获取

# 使用方法

30 秒速记

  • 两组四个入口:ctx.query / ctx.querystring 和 ctx.request.query / ctx.request.querystring,前者代理后者,值一样
  • query → 对象 { a: '1', b: '2' },值是字符串;querystring → 'a=1&b=2',不带 ?
  • 带 ? 的是 ctx.search;路径加查询串是 ctx.url
  • 业务判断用对象,签名校验、原样透传用原始字符串
  • 给 ctx.query 赋值会同步改写 querystring 和 url

Koa 拿 GET 参数就两种形态:query 是解析好的对象,querystring 是原始字符串,两者都能从 ctx 或 ctx.request 上取。 ctx.query 其实就是代理到 ctx.request.query,两种写法拿到的是同一个东西,团队统一一种就行,我习惯直接写 ctx.query。请求 /?a=1&b=2 时,ctx.query 是 { a: '1', b: '2' },注意值是字符串不是数字;ctx.querystring 是 'a=1&b=2',不带问号。业务逻辑用对象,要拿原始文本去算签名时才用 querystring。

在koa中,获取GET请求数据源头是koa中request对象中的query方法或querystring方法,query返回是格式化好的参数对象,querystring返回的是请求字符串,由于ctx对request的API有直接引用的方式,所以获取GET请求数据有两个途径。

  • 1.是从上下文中直接获取
    • 请求对象ctx.query,返回如 { a:1, b:2 }
    • 请求字符串 ctx.querystring,返回如 a=1&b=2
  • 2.是从上下文的request对象中获取
    • 请求对象ctx.request.query,返回如 { a:1, b:2 }
    • 请求字符串 ctx.request.querystring,返回如 a=1&b=2

面试官追问

追问 1ctx.querystring.a 为什么是 undefined?
参考回答

querystring 是字符串 'a=1&b=2',字符串上没有 a 这个属性。要拿字段用 ctx.query.a。

追问 2ctx.query.a === 1 为什么是 false?
参考回答

值是字符串 '1',严格相等比不过。写 Number(ctx.query.a) === 1,或者在入口统一转好类型。

追问 3网关要用原始参数串算签名,用 query 还是 querystring?
参考回答

用 ctx.querystring。解析成对象再拼回去,参数顺序、+ 和 %20 的写法、空值都可能变,签名就对不上了。

追问 4ctx.query 能直接赋值改参数吗?
参考回答

能,ctx.query = { a: '2' } 会反过来改写 ctx.querystring 和 ctx.url,后面的中间件看到的都是新值。一般只在做参数规范化时这么干,业务代码里别随手改。

追问 5反复读 ctx.query,会不会每次都重新解析?
参考回答

不会。Koa 按 querystring 做了缓存,同一个请求里读多少次都只解析一次,ctx.query 和 ctx.request.query 拿到的也是同一份结果。

# 举个例子

30 秒速记

  • 示例把 ctx.url、ctx.request.query、ctx.request.querystring、ctx.query、ctx.querystring 一起返回,方便对照
  • 访问 /page/user?a=1&b=2:url 是 '/page/user?a=1&b=2',query 是 { a: '1', b: '2' },querystring 是 'a=1&b=2'
  • ctx.xxx 和 ctx.request.xxx 两组输出一模一样,前者就是代理
  • 跑起来顺手试边界:重复键、空值、+、中文、不完整的 % 编码

这个例子的思路是把能拿到参数的几个入口一次性全返回,在浏览器里一眼对比它们的区别。 访问 /page/user?a=1&b=2,url 是带路径和查询串的完整地址,两个 query 都是 { a: '1', b: '2' },两个 querystring 都是 'a=1&b=2'。两组完全一致,说明 ctx.query 就是 ctx.request.query 的快捷方式。跑起来之后我建议多试几个边界输入,看看解析结果,比背文档记得牢。

demo源码

https://github.com/poetries/daily-code-practice/tree/master/node/koa/koa2-demo/request/get.js

几个边界输入在 Koa 2 里的实际解析结果,可以对照着自己跑一遍:

// /page/user?a=1&a=2        ctx.query => { a: ['1', '2'] }     重复键变数组
// /page/user?a=&b           ctx.query => { a: '', b: '' }      空值是空字符串
// /page/user?name=Tom+Lee   ctx.query => { name: 'Tom Lee' }   + 当成空格
// /page/user?q=%E4%B8%AD    ctx.query => { q: '中' }
// /page/user?a[b]=1         ctx.query => { 'a[b]': '1' }       不支持嵌套
// /page/user                ctx.query => {}                    没参数是空对象
@前端进阶之旅: 代码已经复制到剪贴板

记住三条就够用:值永远是字符串或字符串数组;没传是 undefined、传了空值是 '',这两种要区别对待;需要嵌套对象就别指望默认解析,换 koa-qs 或者改用 POST 传 JSON。

← 2.2 koa router中间3.2 POST请求数据获取 →

fe